﻿using XinLife.Application.Interface;
using XinLife.Model;
using System.Security.Claims;
using System.Security.Principal;
using XinLife.Core.Extention;
using XinLife.Core.Model;
using XinLife.Core.Helper;
using XinLife.Model.Model;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Http;
using SqlSugar;
using Newtonsoft.Json.Linq;
using Microsoft.Extensions.Logging;
using System.Text.RegularExpressions;

namespace XinLife.Application.Implement
{
    public class UserService : BaseService, IUserService
    {
        private IUser _user;
        private IOAuthService _oAuthService;
        private IRoleService _roleService;
        private JwtOption _jwtOption;
        private IModuleService _moduleService;
        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly IHttpClientFactory _httpClientFactory;
        private readonly ILogger<UserService> _logger;
        public UserService(IBaseRepository baseRepository, IOAuthService oAuthService, IUser user, IRoleService roleService, IModuleService moduleService, JwtOption jwtOption, IHttpContextAccessor httpContextAccessor, IHttpClientFactory httpClientFactory, ILogger<UserService> logger) : base(baseRepository)
        {
            _user = user;
            _logger = logger;
            _oAuthService = oAuthService;
            _roleService = roleService;
            _moduleService = moduleService;
            _jwtOption = jwtOption;
            _httpContextAccessor = httpContextAccessor;
            _httpClientFactory = httpClientFactory;
        }

        public async Task<Tuple<User, IPrincipal>> Authenticate(string userName, string password)
        {
            var user = await GetUserRoleNameStr(userName, password);

            if (user == null)
            {
                return null;
            }

            var claims = new List<Claim> {
                    new Claim(ClaimTypes.NameIdentifier, user.cId),
                     new Claim("roleId", string.Join(",",user.Roles.Select(t=>t.cId).ToArray())),
                    new Claim(ClaimTypes.Name, user.cName),
                    new Claim(ClaimTypes.Expiration, DateTime.Now.AddSeconds(60).ToString()) };
            claims.AddRange(user.Roles.Select(s => new Claim(ClaimTypes.Role, s.cName)));

            ClaimsIdentity identity = new ClaimsIdentity(claims);

            return new Tuple<User, IPrincipal>(user, new ClaimsPrincipal(identity));
        }

        public async Task<AjaxResult> DelUserAsync(string ids)
        {
            string[] cIds = ids.Split(',');
            var res = await _db.Deleteable<User>().In(cIds).ExecuteCommandAsync();
            await _db.Deleteable<UserRole>().Where(t => cIds.Contains(t.cUserId)).ExecuteCommandAsync();
            if (res > 0)
            {
                return Success("删除成功");
            }
            else
            {
                return Error("删除失败");
            }
        }
        public async Task<AjaxResult> ResetPwd(string id)
        {
            await _db.Updateable<User>()
                     .SetColumns(t => t.cPassWord, SecurityUtils.EncryptMD5("123456"))
                     .Where(t => t.cId == id)
                     .ExecuteCommandAsync();
            return Success("ok");
        }



        public async Task<AjaxResult> EditUserAsync(UserInputDto inputUser)
        {
            User user = inputUser.MapTo<User>();

            if (string.IsNullOrWhiteSpace(user.cId))
            {
                user.cId = Guid.NewGuid().ToString();
                user.Create(AspNetUser.UserId);
                user.cPassWord = SecurityUtils.EncryptMD5("123456");
                var r = await _db.Queryable<User>().Where(t => t.cUserName == user.cUserName).ToListAsync();
                if (r.Any())
                {
                    return Error("此登录名已存在不能重复添加");
                }

                UserRole[] userRoles = inputUser.Roles.Select(t => new UserRole(user.cId, t)
                {
                    cCreatedBy = AspNetUser.UserId
                }).ToArray();

                await _db.Insertable(userRoles).ExecuteCommandAsync();

                await _db.Insertable(user).ExecuteCommandAsync();

                return Success("添加成功");
            }
            else
            {
                user.Update(AspNetUser.UserId);

                var r = await _db.Queryable<User>().Where(t => t.cUserName == user.cUserName && t.cId != user.cId).ToListAsync();
                if (r.Any())
                {
                    return Error("此角色名已存在不能修改");
                }

                UserRole[] userRoles = inputUser.Roles.Select(t => new UserRole(user.cId, t)
                {
                    cCreatedBy = AspNetUser.UserId

                }).ToArray();
                var model = await _db.Queryable<User>().InSingleAsync(inputUser.cId);
                model.cUserName = inputUser.cUserName;
                model.cName = inputUser.cName;
                model.cAge = inputUser.cAge;
                model.cRegionCode = inputUser.cRegionCode;
                model.cRegionName = inputUser.cRegionName;
                model.cGender = inputUser.cGender;
                model.cTel = inputUser.cTel;
                model.cEnabled = inputUser.cEnabled;
                model.Update(AspNetUser.UserId);

                try
                {
                    await _db.Ado.BeginTranAsync();

                    await _db.Deleteable<UserRole>().Where(t => t.cUserId == user.cId).ExecuteCommandAsync();
                    await _db.Insertable(userRoles).ExecuteCommandAsync();
                    await _db.Updateable(model).ExecuteCommandAsync();

                    await _db.Ado.CommitTranAsync();
                }
                catch (Exception ex)
                {
                    await _db.Ado.RollbackTranAsync();
                    throw ex;
                }
                return Success("修改成功");
            }
        }

        public async Task<AjaxResult> GetUserByUserId(string userId)
        {
            var user = await _db.Queryable<User>()
                     .LeftJoin<Region>((a, b) => a.cRegionCode == b.cRegionCode)
                     .LeftJoin<Region>((a, b, c) => c.cId == b.cParentId)
                     .LeftJoin<Region>((a, b, c, d) => d.cId == c.cParentId)
                     .Where(a => a.cId == userId)
                     .Select((a, b, c, d) => new UserOutputDto
                     {
                         cId = a.cId.SelectAll(),
                         cCityCode = c.cRegionCode,
                         cCityName = c.cRegionName,
                         cProvinceCode = d.cRegionCode,
                         cProvinceName = d.cRegionName
                     }).FirstAsync();

            user.UserRoles = await _db.Queryable<Role>()
                                     .LeftJoin<UserRole>((a, b) => a.cId == b.cRoleId)
                                     .Where((a, b) => b.cUserId == userId)
                                     .Select((a, b) => new UserRoleOutputDto { RoleId = a.cId, RoleName = a.cName })
                                     .ToListAsync();

            return Success(user);
        }

        public async Task<PageData<UserOutputDto>> GetUserPageList(Pagination page, string queryJson)
        {
            var queryParam = queryJson.ToJObject();

            string? username = queryParam.Value<string>("username");
            string? name = queryParam.Value<string>("name");
            string? sex = queryParam.Value<string>("sex");

            RefAsync<int> totalCount = 0;
            var users = await _db.Queryable<User>()
                     .LeftJoin<Region>((a, b) => a.cRegionCode == b.cRegionCode)
                     .LeftJoin<Region>((a, b, c) => c.cId == b.cParentId)
                     .LeftJoin<Region>((a, b, c, d) => d.cId == c.cParentId)
                     .WhereIF(!username.IsEmpty(), a => a.cUserName.Contains(username))
                     .WhereIF(!name.IsEmpty(), a => a.cName.Contains(name))
                     .WhereIF(!sex.IsEmpty() && sex != "0", a => a.cGender.ToString() == sex)
                     .OrderBy((a) => a.cCreatedDate, OrderByType.Desc)//倒序 
                     .Select((a, b, c, d) => new UserOutputDto
                     {
                         cId = a.cId.SelectAll(),
                         cCityCode = c.cRegionCode,
                         cCityName = c.cRegionName,
                         cProvinceCode = d.cRegionCode,
                         cProvinceName = d.cRegionName
                     })
                     .ToPageListAsync(page.pageIndex, page.pageSize, totalCount);

            return new PageData<UserOutputDto>()
            {
                count = totalCount,
                entitys = users
            };
        }

        public async Task<User> GetUserRoleNameStr(string cUserName, string loginPWD)
        {
            var roles = new List<Role>();
            var user = await _db.Queryable<User>().FirstAsync(t => t.cUserName == cUserName && t.cPassWord == loginPWD);
            if (user != null)
            {
                var userRoles = await _db.Queryable<UserRole>().Where(t => t.cUserId == user.cId).ToListAsync();
                if (userRoles.Any())
                {
                    var roleIds = userRoles.Select(ur => ur.cRoleId.ToString()).ToList();
                    user.Roles = await _db.Queryable<Role>().Where(t => roleIds.Contains(t.cId)).ToListAsync();
                }
            }
            return user;
        }

        public async Task<AjaxResult> QQLogin(string code)
        {
            QQAuth qq = await _oAuthService.QQLogin(code);
            if (qq == null) return Error("登陆失败");

            var user = new User() { cId = qq.userId };
            var role = await _db.Queryable<Role>().FirstAsync(t => t.cName == "QQ用户");
            if (qq.userId == null)
            {
                user = new User()
                {
                    cId = Guid.NewGuid().ToString(),
                    cUserName = qq.openid,
                    cAge = 18,
                    cGender = qq.gender == "男" ? 1 : 2,
                    cName = qq.nickname,
                    cRegionName = qq.city,
                    cPassWord = SecurityUtils.EncryptMD5("123456")
                };
                user.Create("system");
                var userRole = new UserRole(user.cId, role.cId);
                await _db.Insertable(userRole).ExecuteCommandAsync();
                await _db.Insertable(user).ExecuteCommandAsync();
                await _db.Updateable<QQAuth>().SetColumns(t => t.userId, user.cId).Where(t => t.cId == qq.cId).ExecuteCommandAsync();
            }
            user = await GetUserById(user.cId);

            var menus = await _moduleService.GetUserMenu(user.cId, user.Roles.Any(t => t.cName.Contains("超级管理员")));
            var dicButton = await _moduleService.GetUserMenuButtons(user.cId, user.Roles.Any(t => t.cName.Contains("超级管理员")));
            await UpdateLoginTime(user.cId);

            var claims = new List<Claim> {
                    new Claim(ClaimTypes.NameIdentifier,user.cId),
                    new Claim(ClaimTypes.Name, user.cUserName),
                    new Claim(ClaimTypes.Expiration, DateTime.Now.AddSeconds(_jwtOption.Expiration).ToString()) };
            //cookie认证
            var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
            var principal = new ClaimsPrincipal(identity);

            //2. 进行JWT签名
            var token = JwtToken.BuildJwtToken(claims.ToArray(), _jwtOption);
            AspNetUser.User = user;
            await LoginInfo();

            var buttons = new Dictionary<string, List<object>>();
            var buttonList = await _moduleService.GetUserModuleButtons(user.cId, user.Roles.Any(t => t.cName.Contains("超级管理员")));
            foreach (var item in buttonList)
            {
                if (item.cModuleName == null) continue;

                if (!buttons.Keys.Contains(item.cModuleId))
                {
                    buttons[item.cModuleId] = new List<object>();
                }
                buttons[item.cModuleId].Add(new { item.cIcon, item.cTitle, item.cEnCode, item.cPosition });
            }

            return Success("登陆成功",
                    new
                    {
                        user = new { cName = user.cName, cUserName = user.cUserName, cId = user.cId, roles = user.Roles.Select(t => t.cName) },
                        auth_token = token,
                        modules = menus,
                        buttons
                    });
        }
        public async Task<User> GetUserById(string cUserId)
        {
            var roles = new List<Role>();

            var user = await _db.Queryable<User>().InSingleAsync(cUserId);

            if (user != null)
            {
                var userRoles = await _db.Queryable<UserRole>().Where(t => t.cUserId == cUserId).ToListAsync();
                if (userRoles.Any())
                {
                    var ids = userRoles.Select(ur => ur.cRoleId.ToString());
                    user.Roles = await _db.Queryable<Role>().Where(t => ids.Contains(t.cId)).ToListAsync();
                }
            }
            return user;
        }

        public async Task<AjaxResult> SetPwd(string oldPassword, string password)
        {
            var user = await _db.Queryable<User>().FirstAsync(T => T.cId == AspNetUser.UserId);

            if (user.cPassWord != SecurityUtils.EncryptMD5(oldPassword))
            {
                return Error("旧密码错误，请重新再试");
            }
            else
            {
                user.cPassWord = SecurityUtils.EncryptMD5(password);
                await _db.Updateable(user).UpdateColumns(it => new { it.cPassWord }).ExecuteCommandAsync();
                return Success("修改成功");
            }
        }

        public async Task<AjaxResult> UpdateLoginTime(string userId)
        {
            await _db.Updateable<User>()
                         .SetColumns(a => a.cLastLoginTime, DateTime.Now)
                         .Where(a => a.cId == userId)
                         .ExecuteCommandAsync();
            return Success("修改成功");
        }


        public async Task<AjaxResult> LoginInfo()
        {
            HttpContext? context = _httpContextAccessor.HttpContext;
            if (context != null)
            {
                LoginInfo loginInfo = new LoginInfo();
                loginInfo.cIp = context.Connection.RemoteIpAddress?.MapToIPv4()?.ToString();
                loginInfo.cUser = AspNetUser.User.cUserName;

                if (loginInfo.cIp == "0.0.0.1")
                {
                    return Success("ok");
                }
                //if (context.Connection.RemoteIpAddress.Equals(context.Connection.LocalIpAddress))
                //{
                //    return Success("ok");
                //}
                //if (IPAddress.IsLoopback(context.Connection.RemoteIpAddress))
                //{
                //    return Success("ok");
                //}

                string forwardedFor = context.Request.Headers["X-Forwarded-For"];
                if (forwardedFor != null)
                {
                    string[] ipAddresses = forwardedFor!.Split(',', StringSplitOptions.RemoveEmptyEntries);
                    string? proxy_ip = ipAddresses?.FirstOrDefault()?.Trim();

                    if (!proxy_ip.IsEmpty())
                    {
                        loginInfo.cIp = proxy_ip;
                    }
                }
                var handler = new HttpClientHandler
                {
                    ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
                };
                var httpClient = new HttpClient(handler);
                httpClient.DefaultRequestHeaders.Remove("Authorization");
                httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Origin", "https://www.baidu.com");
                httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Host", "qifu-api.baidubce.com");
                httpClient.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.200");

                // string url = $"https://ip.useragentinfo.com/jsonp?ip={loginInfo.cIp}";
                string url = $"https://qifu-api.baidubce.com/ip/geo/v1/district?ip={loginInfo.cIp}";
                try
                {
                    var res = await httpClient.GetStringAsync(url);
                    Regex regex = new Regex(@"callback\((.*)\);");
                    Match match = regex.Match(res);
                    _logger.LogInformation(res);
                    //if (match.Success)
                    //{
                    //    string jsonData = match.Groups[1].Value;
                    //    var jObject = JObject.Parse(jsonData);
                    //    if (jObject.Value<int>("code") == 200)
                    //    {
                    //        loginInfo.cAddress = $"{jObject.Value<string>("province")}|{jObject.Value<string>("city")}|{jObject.Value<string>("area")}|{jObject.Value<string>("isp")}";
                    //    }
                    //}
                    var jObject = JObject.Parse(res);
                    if (jObject.Value<string>("code") == "Success")
                    {
                        loginInfo.cAddress = $"{jObject["data"].Value<string>("prov")}|{jObject["data"].Value<string>("city")}|{jObject["data"].Value<string>("district")}|{jObject["data"].Value<string>("isp")}";
                    }
                    var userAgent = context.Request.Headers["User-Agent"];

                    // 解析用户代理字符串并提取浏览器版本和操作平台信息
                    var parser = UAParser.Parser.GetDefault();
                    var clientInfo = parser.Parse(userAgent);

                    loginInfo.cBrowserName = clientInfo.UA.Family;
                    loginInfo.cBrowserVersion = clientInfo.UA.Major;
                    //windows
                    loginInfo.cOSName = clientInfo.OS.Family;
                    //10
                    loginInfo.cOSVersion = clientInfo.OS.Major;

                    await _db.Insertable(loginInfo).ExecuteCommandAsync();
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex.ToString());
                }
            }
            return Success("ok");
        }
    }
}
